﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace IMModel.Domain.Common
{
    /// <summary>
    /// Реализует вычисление статистических показателей для типов, поддерживающих IRespondentIndexVector
    /// </summary>
    public static class IndexStatistics
    {


        private static double CalculateAvarage(this IRespondentIndexVector source, Func<KeyValuePair<Respondent, double>, double> valueSelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(valueSelector != null);
            Contract.Requires(HasIndexes(source));

            return source.Indexes.Average(valueSelector);
        }

        /// <summary>
        /// Получает дисперсию в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <param name="valueSelector">Селектор значений</param>
        /// <returns>Дисперсия группы</returns>
        public static double VarIndexes(this IRespondentIndexVector source, Func<KeyValuePair<Respondent, double>, double> valueSelector)
        {
            Contract.Ensures(Contract.Result<double>() >= 0);
            var avg = CalculateAvarage(source, valueSelector);
            return source.Indexes.Select(valueSelector).Aggregate((x, y) => x +  Math.Pow(avg - y, 2));
        }

        /// <summary>
        /// Получает частоту вхождения значений в вектор
        /// </summary>
        /// <param name="x">Нижняя граница</param>
        /// <param name="p">Верхняя граница</param>
        /// <returns>Число вхождений</returns>
        public static int Freq(this IRespondentIndexVector source, double x, double p, Func<KeyValuePair<Respondent, double>, double> valueSelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(valueSelector != null);
            Contract.Requires(HasIndexes(source));
            Contract.Requires(x <= p);
            Contract.Requires(x <= 1);
            Contract.Requires(p <= 1);
            Contract.Ensures(Contract.Result<int>() >=0);
            return source.Indexes.Count(r => valueSelector(r) >= x && valueSelector(r) < p);
        }
        /// <summary>
        /// Определяет, что вектор индексов не пуст
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Истина, если вектор не пуст</returns>
        public static bool HasIndexes(this IRespondentIndexVector source)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.Indexes != null);
            return source.Indexes.Count() > 0;
        }

        /// <summary>
        /// Получает средний индекс в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Среднее значение индекса в группе</returns>
        public static double AvarageIndex(this IRespondentIndexVector source)
        {
            return CalculateAvarage(source, x => x.Value);
        }

        /// <summary>
        /// Получает средний индекс лидерства в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Среднее значение индекса лидерства в группе</returns>
        public static double AvarageLeaderIndex(this IRespondentIndexVector source)
        {
             return CalculateAvarage(source, x => x.Key.LeaderIndex); 
        }

        /// <summary>
        ///  Получает средний индекс гармонии в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Среднее значение индекса гармонии в группе</returns>
        public static double AvarageHarmonyIndex(this IRespondentIndexVector source)
        {
            return CalculateAvarage(source, x => x.Key.HarmonyIndex); 
        }

        /// <summary>
        /// Получает максимальный индекс в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Максимальное значение индекса в группе</returns>
        public static double MaxIndex(this IRespondentIndexVector source)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.Indexes != null);
            Contract.Requires(source.Indexes.Count() > 0);

            return source.Indexes.MaxValue;
        }
        /// <summary>
        /// Получает минимальный индекс в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Минимальное значение индекса в группе</returns>  
        public static double MinIndex(this IRespondentIndexVector source)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.Indexes != null);
            Contract.Requires(source.Indexes.Count() > 0);

            return source.Indexes.Min(x => x.Value);
        }

        /// <summary>
        /// Получает минимальный индекс гармонии в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Минимальное значение индекса гармонии в группе</returns>
        public static double MinHarmonyIndex(this IRespondentIndexVector source)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.Indexes != null);
            Contract.Requires(source.Indexes.Count() > 0);

            return source.Indexes.Min(x => x.Key.HarmonyIndex);
        }

        /// <summary>
        /// Получает максимальный индекс гармонии в группе
        /// </summary>
        /// <param name="source">Вектор индексов</param>
        /// <returns>Максимальное значение индекса гармонии в группе</returns>
        public static double MaxHarmonyIndex(this IRespondentIndexVector source)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.Indexes != null);
            Contract.Requires(source.Indexes.Count() > 0);

            return source.Indexes.Max(x => x.Key.HarmonyIndex);
        }
        
    }
}